import { Component, Input, AfterViewInit, ViewChild, Renderer2, ElementRef, OnChanges, OnInit } from '@angular/core';

@Component({
    selector: 'risk-details',
    templateUrl: './risk-details.component.html',
})
export class RiskDetailsComponent implements AfterViewInit, OnInit, OnChanges {
    @Input() details: {
        exposure: number,
        manageableRisk: number,
        managedRisk: number
    };
    @Input() isMei: boolean;

    @ViewChild('canvasContainer') public canvasContainer: ElementRef;
    private cx: CanvasRenderingContext2D;
    private myCanvas;
    private managementGap: number;
    private unmanageableRisk: number;
    private unmanagedRisk: number;
    private titleX = 180;
    private valueX = 215;
    private chartX = 265;
    private guidanceX = 470;
    private guidanceWidth = 410;
    private guidanceLineHeight = 18;
    private startY = 20;
    private currentY = 20;
    private barHeight = 20;
    private barHeightMult = 1.5;
    private chartWidth = 180;
    private barSpacing = 50;
    private width = 900;
    private height = 650;

    private PIXEL_RATIO = (function () {
        const ctx: any = document.createElement('canvas').getContext('2d'),
            dpr = window.devicePixelRatio || 1,
            bsr = ctx.webkitBackingStorePixelRatio ||
                ctx.mozBackingStorePixelRatio ||
                ctx.msBackingStorePixelRatio ||
                ctx.oBackingStorePixelRatio ||
                ctx.backingStorePixelRatio || 1;

        return dpr / bsr;
    })();

    constructor(
        private renderer: Renderer2, private el: ElementRef
    ) { }

    ngOnInit() {
        this.myCanvas = this.createHiDPICanvas(this.width, this.height, this.PIXEL_RATIO);
        this.cx = this.myCanvas.getContext('2d');
        this.cx.clearRect(0, 0, this.width, this.height);
        this.renderer.appendChild(this.canvasContainer.nativeElement, this.myCanvas);
    }

    ngOnChanges() {
        this.currentY = this.startY;
        if (this.cx) {
            this.cx.clearRect(0, 0, this.width, this.height);
            this.drawChart();
        }
    }

    ngAfterViewInit() {
        this.drawChart();
    }

    private drawChart() {
        this.managementGap = this.details.manageableRisk - this.details.managedRisk;
        this.unmanageableRisk = this.details.exposure - this.details.manageableRisk;
        this.unmanagedRisk = this.details.exposure - this.details.managedRisk;

        // table header
        this.drawHeading();

        // exposure
        this.drawTitle('Exposure');
        this.drawExposure();
        const line1Top = this.getCurrentYPosition();

        // management
        this.cx.moveTo(0, this.currentY + 40);
        this.cx.lineTo(this.width, this.currentY + 40);
        this.cx.stroke();
        this.currentY = 170;
        this.drawTitle('Management');

        // manageable risk
        this.drawManageableRisk();
        const line3Top = this.getCurrentYPosition();
        this.currentY += this.barSpacing;

        // managed risk
        this.drawManagedRisk();
        const line2Top = this.getCurrentYPosition();
        this.currentY += this.barSpacing;

        // management gap
        this.drawManagementGap();
        this.currentY += this.barSpacing;

        // unmanageable risk
        this.drawUnmanageableRisk();
        const line3Bottom = this.getCurrentYPosition() + this.barHeight;
        this.currentY += this.barSpacing * 1.5;

        // unmanaged risk
        this.cx.fillStyle = '#ebf1f6';
        this.cx.fillRect(0, this.currentY - this.barSpacing / 2, this.width, 130);
        this.cx.fillStyle = '#000000';
        this.cx.moveTo(0, this.currentY - this.barSpacing / 2);
        this.cx.lineTo(this.width, this.currentY - this.barSpacing / 2);
        this.cx.stroke();

        this.drawUnmanagedRisk();
        const line1Bottom = this.getCurrentYPosition() + this.barHeight;

        /* draw lines */
        // left line
        this.cx.beginPath();
        this.cx.moveTo(this.chartX, line1Top);
        this.cx.lineTo(this.chartX, line1Bottom);
        this.cx.stroke();
        this.cx.closePath();

        // right line
        this.cx.beginPath();
        this.cx.moveTo(this.chartX + this.chartWidth, line1Top);
        this.cx.lineTo(this.chartX + this.chartWidth, line1Bottom);
        this.cx.stroke();
        this.cx.closePath();

        // managed -> unmanaged
        this.cx.beginPath();
        this.cx.moveTo(this.chartX + this.getBarWidth(this.details.managedRisk), line2Top);
        this.cx.lineTo(this.chartX + this.getBarWidth(this.details.managedRisk), line1Bottom);
        this.cx.stroke();
        this.cx.closePath();

        // manageable -> unmanageable
        this.cx.beginPath();
        this.cx.moveTo(this.chartX + this.getBarWidth(this.details.manageableRisk), line3Top);
        this.cx.lineTo(this.chartX + this.getBarWidth(this.details.manageableRisk), line3Bottom);
        this.cx.stroke();
        this.cx.closePath();
    }

    private drawExposure() {
        this.drawValue('Company Exposure', this.details.exposure);
        this.drawBar(this.chartX, this.currentY, this.chartWidth, '#ff9901');
        this.drawGuidance('The company\'s sensitivity or vulnerability to ESG risk.');
    }

    private drawManageableRisk() {
        this.currentY += 20; 
        const width = this.getBarWidth(this.details.manageableRisk);
        this.drawValue('Manageable Risk', this.details.manageableRisk);
        this.drawBar(this.chartX, this.currentY, width, '#b7c42e');
        this.drawGuidance('Material ESG risk that can be influenced and managed through suitable policies, programmes and initiatives.');
        this.drawDashedLine();
    }

    private drawManagedRisk() {
        const width = this.getBarWidth(this.details.managedRisk);
        this.drawValue('Managed Risk', this.details.managedRisk);
        this.drawBar(this.chartX, this.currentY, width, '#dbe196');
        this.drawGuidance('Material ESG risk that has been managed by a company through suitable policies, programmes or initiatives.');
        this.drawDashedLine();
    }

    private drawManagementGap() {
        const x = this.chartX + this.getBarWidth(this.details.managedRisk);
        const width = this.getBarWidth(this.managementGap);
        this.drawValue('Management Gap', this.managementGap);
        this.drawBar(x, this.currentY, width, '#dbe196');
        this.drawGuidance('Measures the difference between material ESG risk that could be managed by the company and what the company is managing.');
        this.drawDashedLine();
    }

    private drawUnmanageableRisk() {
        const x = this.chartX + this.getBarWidth(this.details.managedRisk) + this.getBarWidth(this.managementGap);
        const width = this.getBarWidth(this.unmanageableRisk);
        this.drawValue('Unmanageable Risk', this.unmanageableRisk);
        this.drawBar(x, this.currentY, width, '#dbe196');
        this.drawGuidance('Material ESG risk inherent in the products or services of a company and/or the nature of a company’s business, which cannot be managed by the company.');
    }

    private drawUnmanagedRisk() {
        const x = this.chartX + this.getBarWidth(this.details.managedRisk);
        const width = this.getBarWidth(this.unmanagedRisk);
        this.drawTitle("ESG Risk Rating", false);
        this.drawValue(this.isMei ? 'Issue Unmanaged Risk' : 'Overall Unmanaged Risk', this.unmanagedRisk, false);
        this.drawBar(x, this.currentY, width, '#6c6f70');
        this.drawGuidance('Material ESG risk that has not been managed by a company, and includes two types of risk: unmanageable risk, as well as risks that could be managed by a company through suitable initiatives but which may not yet be managed.');
        return this.currentY;
    }

    private drawHeading() {
        this.cx.font = '20px Arial';
        this.cx.textAlign = 'left';
        this.cx.fillText('Risk Details', 5, this.currentY);

        this.cx.moveTo(0, this.currentY + 20);
        this.cx.lineTo(this.width, this.currentY + 20);
        this.cx.stroke();
        this.currentY += 50;
    }

    private drawTitle(name: string, bold: boolean = false) {
        this.cx.font = bold ? 'bold 16px Arial' : '16px Arial';
        this.cx.textAlign = 'left';
        this.cx.fillText(name, 10, this.currentY);
        this.currentY += 30;
    }

    private drawValue(name: string, value: number, titleFont = false) {
        this.cx.font = titleFont ? '16px Arial' : '13px Arial';
        this.cx.textAlign = titleFont ? 'left' : 'right';
        this.cx.fillText(name, titleFont ? 10 : this.titleX, this.currentY);

        this.cx.font = '13px Arial';
        this.cx.textAlign = 'left';
        this.cx.fillText(value.toFixed(1).toString(), this.valueX, this.currentY);
    }

    private drawBar(x: number, y: number, width: number, color: string) {
        this.cx.fillStyle = color;
        this.cx.fillRect(x, y - this.barHeight / this.barHeightMult, width, this.barHeight);

        // reset color
        this.cx.fillStyle = '#000000';
    }

    private drawDashedLine() {
        this.cx.beginPath();
        this.cx.setLineDash([5, 5]);
        this.cx.strokeStyle = '#999999';
        this.cx.moveTo(this.guidanceX, this.currentY + this.barSpacing / 1.75);
        this.cx.lineTo(this.width, this.currentY + this.barSpacing / 1.75);
        this.cx.stroke();
        this.cx.closePath();

        // reset
        this.cx.beginPath();
        this.cx.setLineDash([]);
        this.cx.strokeStyle = '#000000';
        this.cx.closePath();
    }

    private drawGuidance(text: string) {
        this.cx.font = '13px Arial';
        this.cx.textAlign = 'left';
        this.wrapText(text, this.guidanceX, this.currentY);
    }

    private getBarWidth(value: number) {
        return this.chartWidth * value / this.details.exposure;
    }

    private wrapText(text, x, y) {
        const words = text.split(' ');
        let line = '';
        for (let n = 0; n < words.length; n++) {
            const testLine = line + words[n] + ' ';
            const metrics = this.cx.measureText(testLine);
            const testWidth = metrics.width;
            if (testWidth > this.guidanceWidth) {
                this.cx.fillText(line, x, y);
                line = words[n] + ' ';
                y += this.guidanceLineHeight;
            } else {
                line = testLine;
            }
        }
        this.cx.fillText(line, x, y);
    }

    private createHiDPICanvas(width, height, ratio) {
        if (!ratio) { ratio = this.PIXEL_RATIO; }
        const can = document.createElement('canvas');
        can.width = width * ratio;
        can.height = height * ratio;
        can.style.width = width + 'px';
        can.style.height = height + 'px';
        can.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);
        return can;
    }

    private getCurrentYPosition(): number {
        return this.currentY - this.barHeight / this.barHeightMult;
    }
}
